﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Raytracererer
{
    class Cylinder : Primitive
    {
        Vector3 center;
        Vector3 axisOfSymmetry;
        double height;
        double radius;
        Plane topPlane;
        Plane bottomPlane;

        public Cylinder(Vector3 center, Vector3 axisOfSymmetry, double radius, Color color, double height = double.MaxValue)
        {
            this.center = center;
            this.axisOfSymmetry = axisOfSymmetry.Normalized();
            this.radius = radius;
            this.color = color;
            this.height = height;
            topPlane = new Plane(this.center + this.axisOfSymmetry * height / 2, this.axisOfSymmetry, this.color);
            bottomPlane = new Plane(this.center - this.axisOfSymmetry * height / 2, -1 * this.axisOfSymmetry, this.color);
        }

        Color m_color;
        double m_transparency;
        double m_reflectivity;
        double m_refractiveIndex;
        double m_diffuse;
        Color m_specularity;

        public Color color
        {
            get { return m_color; }
            set { m_color = value; }
        }
        public double transparency
        {
            get { return m_transparency; }
            set { m_transparency = value; }
        }
        public double reflectivity
        {
            get;
            set;
        }
        public double refractionIndex
        {
            get;
            set;
        }
        public double diffuse
        {
            get;
            set;
        }
        public Color specularity
        {
            get;
            set;
        }

        public double Intersect(Ray ray, out Vector3 position)
        {
            var set = new SortedList<double, Vector3>();

            position = null;
            //vi kollar om det finns punkter som satisfierar cylinderns ekvation
            //jag formulerade den som
            // ||(x - c) - ((x - c)*dir)dir ||^2 = R^2
            //vinkelräta projektionen på symmetrilinjen är radien

            Vector3 A = ray.Position - center - ((ray.Position - center) * axisOfSymmetry) * axisOfSymmetry; //alla lösa vektorer
            Vector3 B = ray.Direction - (ray.Direction * axisOfSymmetry) * axisOfSymmetry; //vektorer som multipliceras med t
            double ASquared = A * A;
            double BSquared = B * B;
            double rootSquared = (radius * radius - ASquared) / BSquared + Math.Pow((A * B) / BSquared, 2);
            double distToMantel = -1;
            bool isMantelIntersection = false;
            Vector3 mantelIntersection;

            if (rootSquared > 0)
            {
                double firstTerm = -(A * B) / BSquared;
                double root = Math.Sqrt(rootSquared);
                if ((distToMantel = firstTerm - root) > 0 || (distToMantel = firstTerm + root) > 0)
                {
                    mantelIntersection = ray.Position + distToMantel * ray.Direction;
                    if (Math.Abs((mantelIntersection - center) * axisOfSymmetry) <= height / 2)
                        set.Add(distToMantel, mantelIntersection);
                        isMantelIntersection = true;
                }
            }
            //nu har vi kollar klart på manteln
            Vector3 intersectWithTop;
            Vector3 intersectWithBottom;
            double distToTop = topPlane.Intersect(ray, out intersectWithTop);
            double distToBottom = bottomPlane.Intersect(ray, out intersectWithBottom);
            
            if (distToTop != -1 && (intersectWithTop - topPlane.point).LengthSquared() <= radius * radius)
                set.Add(distToTop, intersectWithTop);
            if (distToBottom != -1 && (intersectWithBottom - bottomPlane.point).LengthSquared() <= radius * radius)
                set.Add(distToBottom, intersectWithBottom);
            
            if (set.Count==0)
                return -1;
            position = set.First().Value;
            return set.First().Key;

            //annars måste vi hantera dem suck

        }

        public Vector3 Normal(Vector3 position)
        {
            return ((position - center) - ((position - center) * axisOfSymmetry) * axisOfSymmetry).Normalized();
        }
        public virtual Color ColorAt(Vector3 position)
        {
            return m_color;
        }

        public bool isIn(Vector3 position)
        {
            return false;
        }
    }
}
